// ============================================================================
//
// Copyright (C) 2006-2007 Dengues
//
// Google Group: http://groups.google.com/group/dengues
// QQ Group: 24885404
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// ============================================================================
package org.dengues.core.export;

import static org.dengues.core.runprocess.JavaProcessorUtil.CUSTOMERAPI_PKG;
import static org.dengues.core.runprocess.JavaProcessorUtil.DENGUESAPI_PKG;
import static org.dengues.core.runprocess.JavaProcessorUtil.JAVA_DENGUES_PKG;
import static org.dengues.core.runprocess.JavaProcessorUtil.JAVA_PROJ_CODE_PKG;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.dengues.commons.utils.FileUtils;
import org.dengues.core.DenguesCorePlugin;
import org.dengues.core.ExceptionOperation;
import org.dengues.core.IDesignerCoreService;
import org.dengues.core.i18n.Messages;
import org.dengues.core.module.EMFModule;
import org.dengues.core.module.IModuleScriptSynchronizer;
import org.dengues.core.process.ICompProcess;
import org.dengues.core.resource.DenguesProjectFactory;
import org.dengues.core.runprocess.IJavaETLProcessor;
import org.dengues.core.runprocess.JavaProcessorUtil;
import org.dengues.model.warehouse.ProcessType;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaModelException;

/**
 * Manages the job scripts to be exported. <br/>
 * 
 * 
 * Qiang.Zhang.Adolf@gmail.com class global comment. Detailled comment <br/>
 * 
 * $Id: Dengues.epf Qiang.Zhang.Adolf@gmail.com 2008-4-10 qiang.zhang $
 * 
 */
public class EtlProcessScriptsManager {

    public static final String PATH_SEPARATOR = "/"; //$NON-NLS-1$

    private static final String USER_ROUTINES_PATH = JAVA_DENGUES_PKG + PATH_SEPARATOR + CUSTOMERAPI_PKG;

    private static final String SYSTEM_ROUTINES_PATH = JAVA_DENGUES_PKG + PATH_SEPARATOR + DENGUESAPI_PKG;

    // private static final String JOB_CONTEXT_FOLDER = "contexts";

    private static final String SYSTEMROUTINE_JAR = "systemRoutines.jar"; //$NON-NLS-1$

    private static final String USERROUTINE_JAR = "userRoutines.jar"; //$NON-NLS-1$

    private static final String UNIX_LAUNCHER = "run.sh"; //$NON-NLS-1$

    private static final String WINDOWS_LAUNCHER = "run.bat"; //$NON-NLS-1$

    private static final String LIBRARY_FOLDER_NAME = "lib"; //$NON-NLS-1$

    public static final String ALL_ENVIRONMENTS = "All"; //$NON-NLS-1$

    public static final String UNIX_ENVIRONMENT = "Unix"; //$NON-NLS-1$

    public static final String WINDOWS_ENVIRONMENT = "Windows"; //$NON-NLS-1$

    private String windowsCmd;

    private String unixCmd;

    private static IFile codeFile;

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getTmpFolder".
     * 
     * @return
     */
    private String getTmpFolder() {
        String tmpFold = getTmpFolderPath();
        File f = new File(tmpFold);
        if (!f.exists()) {
            f.mkdir();
        }
        return tmpFold;
    }

    private String getTmpFolderPath() {
        String tmpFolder = System.getProperty("user.dir"); //$NON-NLS-1$
        tmpFolder = tmpFolder + "/etlProcessExporter"; //$NON-NLS-1$
        return tmpFolder;
    }

    /**
     * 
     * Create launcher(s) and get url(s).
     * 
     * @param process
     * @param environment use JobScriptsManager.ALL_ENVIRONMENTS, JobScriptsManager.UNIX_ENVIRONMENT or
     * JobScriptsManager.WINDOWS_ENVIRONMENT
     * @param statisticPort TODO
     * @param tracePort TODO
     * @param codeOptions TODO
     * @return
     */
    private List<URL> getLauncher(ProcessType process, String environment) {

        List<URL> list = new ArrayList<URL>();
        String tmpFold = getTmpFolder();

        if (environment.equals(ALL_ENVIRONMENTS)) {
            createLauncherFile(process, list, unixCmd, UNIX_LAUNCHER, tmpFold);
            createLauncherFile(process, list, windowsCmd, WINDOWS_LAUNCHER, tmpFold);
        } else if (environment.equals(UNIX_ENVIRONMENT)) {
            createLauncherFile(process, list, unixCmd, UNIX_LAUNCHER, tmpFold);
        } else if (environment.equals(WINDOWS_ENVIRONMENT)) {
            createLauncherFile(process, list, windowsCmd, WINDOWS_LAUNCHER, tmpFold);
        }

        return list;
    }

    private void createLauncherFile(ProcessType process, List<URL> list, String cmdPrimary, String fileName, String tmpFold) {
        PrintWriter pw = null;
        try {

            File file = new File(tmpFold, process.getName() + "_" + fileName); //$NON-NLS-1$
            file.createNewFile();
            pw = new PrintWriter(new FileOutputStream(file));
            pw.print(cmdPrimary);
            pw.flush();
            list.add(file.toURL());
            pw.close();
        } catch (Exception e) {
            ExceptionOperation.operate(e);
        } finally {
            try {
                if (pw != null) {
                    pw.close();
                }
            } catch (Exception e) {
                // do nothing here
            }
        }
    }

    /**
     * Deletes the temporary files.
     */
    public void deleteTempFiles() {
        String tmpFold = getTmpFolderPath();
        File file = new File(tmpFold);
        if (!file.exists() && !file.isDirectory()) {
            return;
        }
        deleteDirectory(file);
        if (codeFile != null && codeFile.exists()) {
            try {
                codeFile.delete(true, null);
                // codeFile.getProject().refreshLocal(IResource.DEPTH_ONE, null);
            } catch (CoreException e) {
                ExceptionOperation.operate(e);
            }
            codeFile = null;
        }
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "deleteDirectory".
     * 
     * @param dir
     */
    private void deleteDirectory(File dir) {
        File[] entries = dir.listFiles();
        int sz = entries.length;
        for (int i = 0; i < sz; i++) {
            if (entries[i].isDirectory()) {
                deleteDirectory(entries[i]);
            } else {
                entries[i].delete();
            }
        }
        dir.delete();
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getExportResources".
     * 
     * @param exportResList
     * @param monitor
     * @throws IOException
     * @throws MalformedURLException
     */
    public List<ExportFileResource> getExportResources(List<ExportFileResource> exportResList, IProgressMonitor monitor)
            throws MalformedURLException, IOException {
        for (int i = 0; i < exportResList.size(); i++) {
            ExportFileResource exportFileResource = exportResList.get(i);
            ProcessType processItem = exportFileResource.getItem();
            String libPath = calculateLibraryPathFromDirectory(exportFileResource.getPath().toPortableString());
            // use character @ as temporary classpath separator, this one will be replaced during the export.
            String standardJars = libPath + PATH_SEPARATOR + SYSTEMROUTINE_JAR + JavaProcessorUtil.TEMP_JAVA_CLASSPATH_SEPARATOR
                    + libPath + PATH_SEPARATOR + USERROUTINE_JAR + JavaProcessorUtil.TEMP_JAVA_CLASSPATH_SEPARATOR + "." //$NON-NLS-1$
                    + JavaProcessorUtil.TEMP_JAVA_CLASSPATH_SEPARATOR;
            monitor.subTask(Messages.getString("EtlProcessScriptsManager.genCode")); //$NON-NLS-1$

            generateETLProcessCode(processItem, exportFileResource, libPath, standardJars);
            monitor.subTask(Messages.getString("EtlProcessScriptsManager.getJars")); //$NON-NLS-1$
            List<URL> resources = new ArrayList<URL>();
            resources.addAll(getLauncher(processItem, FileUtils.escapeSpace(ALL_ENVIRONMENTS)));
            String pName = FileUtils.escapeSpace(processItem.getName());
            resources.addAll(getEtlScripts(pName, true));
            exportFileResource.addResources(processItem.getName().toLowerCase(), resources);
        }

        // Exports the system libs
        List<ExportFileResource> list = new ArrayList<ExportFileResource>(exportResList);
        // Add the java system libraries
        ExportFileResource rootResource = new ExportFileResource(null, LIBRARY_FOLDER_NAME);
        list.add(rootResource);
        // Gets system routines
        List<URL> systemRoutineList = getSystemRoutine();
        rootResource.addResources(LIBRARY_FOLDER_NAME, systemRoutineList);
        // Gets user routines
        List<URL> userRoutineList = getUserRoutine();
        rootResource.addResources(LIBRARY_FOLDER_NAME, userRoutineList);

        return list;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getUserRoutine".
     * 
     * @return
     */
    private List<URL> getUserRoutine() {
        List<URL> list = new ArrayList<URL>();
        try {
            String classRoot = getClassRootLocation();
            List<String> include = new ArrayList<String>();
            include.add(USER_ROUTINES_PATH);

            List<String> excludes = new ArrayList<String>();
            excludes.add(SYSTEM_ROUTINES_PATH);

            String jarPath = getTmpFolder() + PATH_SEPARATOR + USERROUTINE_JAR;

            // make a jar file of system routine classes
            JarBuilder jarbuilder = new JarBuilder(classRoot, jarPath);
            jarbuilder.setIncludeDir(include);
            jarbuilder.setExcludeDir(excludes);

            jarbuilder.buildJar();

            File jarFile = new File(jarPath);
            URL url = jarFile.toURL();
            list.add(url);
        } catch (Exception e) {
            ExceptionOperation.operate(e);
        }
        return list;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getSystemRoutine".
     * 
     * @return
     */
    private List<URL> getSystemRoutine() {
        List<URL> list = new ArrayList<URL>();
        try {
            String classRoot = getClassRootLocation();
            List<String> include = new ArrayList<String>();
            include.add(SYSTEM_ROUTINES_PATH);

            String jarPath = getTmpFolder() + PATH_SEPARATOR + SYSTEMROUTINE_JAR;

            // make a jar file of system routine classes
            JarBuilder jarbuilder = new JarBuilder(classRoot, jarPath);
            jarbuilder.setIncludeDir(include);

            jarbuilder.buildJar();

            File jarFile = new File(jarPath);
            URL url = jarFile.toURL();
            list.add(url);
        } catch (Exception e) {
            ExceptionOperation.operate(e);
        }
        return list;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "generateETLProcessCode".
     * 
     * @param processItem
     * @param exportFileResource.getPath()
     * @param standardJars
     * @param libPath
     * @throws IOException
     * @throws MalformedURLException
     */
    private void generateETLProcessCode(ProcessType processItem, ExportFileResource exportFileResource, String libPath,
            String standardJars) throws MalformedURLException, IOException {
        ICompProcess curProcess = DenguesCorePlugin.getDefault().getDesignerUIService().getCompProcessFromType(processItem,
                exportFileResource.getPath());
        IDesignerCoreService designerCoreService = DenguesCorePlugin.getDefault().getDesignerCoreService();
        IJavaETLProcessor javaProcessor = designerCoreService.getJavaProcessor(curProcess, exportFileResource.getPath());
        javaProcessor.generateCode(false);

        codeFile = javaProcessor.getCodeFile();

        Set<EMFModule> neededLibraries = curProcess.getNeededLibraries(true);
        List<URL> urls = new ArrayList<URL>();
        IModuleScriptSynchronizer scriptSynchronizer = designerCoreService.getScriptSynchronizer();
        Set<EMFModule> scriptAllModules = scriptSynchronizer.getScriptAllModules();
        neededLibraries.addAll(scriptAllModules);
        Set<String> neededLibs = new HashSet<String>();
        for (EMFModule module : neededLibraries) {
            neededLibs.add(module.getLibJar());
        }
        unixCmd = logCommandLine(javaProcessor.getCommandLine(UNIX_ENVIRONMENT, true, libPath, standardJars, neededLibs));
        windowsCmd = logCommandLine(javaProcessor.getCommandLine(WINDOWS_ENVIRONMENT, true, libPath, standardJars, neededLibs));
        for (EMFModule url : neededLibraries) {
            urls.add(FileLocator.toFileURL(url.getUrl()));
        }
        exportFileResource.addResources(LIBRARY_FOLDER_NAME, urls);
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "logCommandLine".
     * 
     * @param cmd
     * @return
     */
    private String logCommandLine(String[] cmd) {
        StringBuffer sb = new StringBuffer();
        for (String s : cmd) {
            sb.append(' ').append(s);
        }
        return sb.toString();
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "calculateLibraryPathFromDirectory".
     * 
     * @param directory
     * @return
     */
    private String calculateLibraryPathFromDirectory(String directory) {
        int nb = directory.split(PATH_SEPARATOR).length - 1;
        String path = "../"; //$NON-NLS-1$
        for (int i = 0; i < nb; i++) {
            path = path.concat("../"); //$NON-NLS-1$
        }
        return path + LIBRARY_FOLDER_NAME;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getEtlScripts".
     * 
     * @param jobName
     * @param needJob
     * @return
     */
    private List<URL> getEtlScripts(String jobName, boolean needJob) {
        List<URL> list = new ArrayList<URL>(1);
        if (!needJob) {
            return list;
        }
        String projectName = getCurrentProjectName();
        jobName = jobName.toLowerCase();

        try {
            String classRoot = getClassRootLocation();
            String jarPath = getTmpFolder() + PATH_SEPARATOR + jobName + ".jar"; //$NON-NLS-1$
            // Exports the jar file
            JarBuilder jarbuilder = new JarBuilder(classRoot, jarPath);

            // builds the jar file of the job classes,needContext specifies whether inclucdes the context.
            // add the job
            String jobPath = JAVA_DENGUES_PKG + PATH_SEPARATOR + JAVA_PROJ_CODE_PKG + PATH_SEPARATOR + projectName
                    + PATH_SEPARATOR;
            List<String> include = new ArrayList<String>();
            include.add(jobPath);
            jarbuilder.setIncludeDir(include);
            // filter the context
            // String contextPaht = jobPath + PATH_SEPARATOR + JOB_CONTEXT_FOLDER;
            // List<String> excludes = new ArrayList<String>(1);
            // excludes.add(contextPaht);
            // jarbuilder.setExcludeDir(excludes);

            jarbuilder.buildJar();

            File jarFile = new File(jarPath);
            URL url = jarFile.toURL();
            list.add(url);
        } catch (Exception e) {
            ExceptionOperation.operate(e);
        }
        return list;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getClassRootLocation".
     * 
     * @return
     */
    private String getClassRootLocation() {
        IPath binPath = null;
        try {
            IJavaProject javaProject = DenguesCorePlugin.getDefault().getDesignerCoreService().getJavaProcessor()
                    .getJavaProject();
            binPath = javaProject.getOutputLocation();

            IPath root = javaProject.getProject().getParent().getLocation();
            binPath = root.append(binPath);
            URL url;
            url = binPath.toFile().toURL();
            return url.getPath();
        } catch (MalformedURLException e) {
            ExceptionOperation.operate(e);
        } catch (JavaModelException e) {
            ExceptionOperation.operate(e);
        }

        return null;
    }

    /**
     * Qiang.Zhang.Adolf@gmail.com Comment method "getCurrentProjectName".
     * 
     * @return
     */
    private String getCurrentProjectName() {
        String name = DenguesProjectFactory.getDenguesProject().getName();
        name = name.replaceAll(" ", "_"); //$NON-NLS-1$ //$NON-NLS-2$
        name = name.toLowerCase();
        return name;
    }
}
